package junior.db.dao.update;

import junior.db.dao.base.AbstractFilter;
import junior.db.dao.base.MybatisHelper;
import junior.db.dao.base.IEntityWrapper;
import junior.db.dao.base.IExecuteAware;
import junior.db.dao.base.INamespaceAware;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author wubo
 * @date 2024/8/29 13:36
 */
public abstract class AbstractUpdateOption<O> extends AbstractFilter<O> implements IEntityWrapper, INamespaceAware, IExecuteAware {
    protected String namespace;
    protected String[] updateFields;
    protected Map<String, Object> updateFieldValueMap;
    protected Map<String, String> fieldExpressionMap;
    protected boolean skipNull;
    private List<String> skipNullExceptFields;
    
    public AbstractUpdateOption() {
        this(true);
    }
    
    public AbstractUpdateOption(boolean skipNull) {
        this.skipNull = skipNull;
    }
    
    public String getColumn(String field) {
        return MybatisHelper.field2Column(namespace, entityClass, field);
    }
    
    public String getTable() {
        return MybatisHelper.getTable(namespace, this.entityClass);
    }
    
    public String[] getUpdateFields() {
        return updateFields;
    }
    
    public boolean isSkipNull() {
        return skipNull;
    }
    
    public List<String> getSkipNullExceptFields() {
        return skipNullExceptFields;
    }
    
    public O withSkipNull(boolean skipNull) {
        this.skipNull = skipNull;
        return (O) this;
    }
    public O withSkipNullExceptFields(String... fields) {
        if (skipNullExceptFields == null) {
            skipNullExceptFields = new ArrayList<>();
        }
        for (String f : fields) {
            skipNullExceptFields.add(f);
        }
        return (O) this;
    }
    /**
     * 指定要更新的字段
     * @param fields
     * @return
     */
    public O withUpdateFields(String... fields) {
        Assert.notEmpty(fields, "Parameter fields is required");
        updateFields = fields;
        return (O) this;
    }
    
    public O withUpdateValue(String field, Object value) {
        if (updateFieldValueMap == null) {
            updateFieldValueMap = new HashMap<>();
        }
        updateFieldValueMap.put(field, value);
        return (O) this;
    }
    
    public O withUpdateExpression(String field, String expression) {
        if (fieldExpressionMap == null) {
            fieldExpressionMap = new HashMap<>();
        }
        fieldExpressionMap.put(field, expression);
        return (O) this;
    }
    
    /**
     * 更新时加上版本号，用于乐观锁，
     * 见withVersion(versionField, version, skipOnNull)方法
     * @param versionField 版本号字段名
     * @param version 当前记录中的版本号
     * @return
     */
    public O withVersion(String versionField, Object version) {
        return withVersion(versionField, version, false);
    }
    /**
     * <h3>更新时加上版本号，用于乐观锁</h3>
     * <p>此方法对应的SQL语句部分实际如下：</p>
     * <p>当skipOnNull参数为false, versionField参数为'versionNo'，version = 1时：</p>
     * <p>set version_no = IFNULL(version_no, 0) + 1 where version_no = 1</p>
     * <p>当skipOnNull参数为false, versionField参数为'versionNo'，version = null时：</p>
     * <p>set version_no = IFNULL(version_no, 0) + 1 where version_no is null</p>
     * <p>当skipOnNull参数为true, versionField参数为'versionNo'，version = null时：直接返回，此方法调用将不会起作用</p>
     * @param versionField 版本号字段名
     * @param version 当前记录中的版本号
     * @param skipOnNull 当version为null时是否跳过versionField字段的更新
     * @return
     */
    public O withVersion(String versionField, Object version, boolean skipOnNull) {
        if (version != null || !skipOnNull) {
            this.withUpdateExpression(versionField, "ifNull(${"+versionField+"}, 0) + 1");
            if (version != null) {
                this.eq(versionField, version);
            } else {
                this.nvl(versionField);
            }
        }
        return (O) this;
    }
    
    @Override
    public void setNamespace(String namespace) {
        this.namespace = namespace;
    }
}
